package cn.kbyue.security.security;

import cn.kbyue.security.utils.Env;
import java.io.Serializable;
import java.util.Collection;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

/**
 * 自定义权限注解 验证类
 *
 * @author xl
 * @date 2020/4/19 15:40
 */
@Slf4j
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {

    /**
     * hasPermission 的鉴权方法
     * 其它应用：根据参数动态生成 SQL 条件，用于动态数据权限校验
     *
     * @param authentication 用户身份
     * @param targetUrl 这里应用于请求路径，由注解的参数写入
     * @param permissionCode 权限 code
     * @return
     */
    @Override
    public boolean hasPermission(Authentication authentication, Object targetUrl, Object permissionCode) {
        // 根据用户的身份获取用户的权限信息，对比于接口开放的权限
        log.info(">> targetUrl: {}, permissionCode: {}", targetUrl, permissionCode);

        if (authentication instanceof AnonymousAuthenticationToken) {
            throw new AuthenticationServiceException("未登录, 请先登录");
        }

        return this.hasPermission(authentication, permissionCode);
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        return false;
    }

    private boolean hasPermission(Authentication authentication, Object permissionCode) {
        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
        final String permitCode = (String) permissionCode;

        if (Env.isDebug) {
            log.info(">>> authorities : {}, permissionCode: {} ", authorities, permissionCode);
        }

        for (GrantedAuthority authority: authorities) {
            if (authority instanceof SimpleGrantedAuthority) {
                continue;
            }
            DefinedGrantedAuthority auth = (DefinedGrantedAuthority) authority;
            boolean hasPermit = auth.getPermission().stream().anyMatch(permit -> permit.contains(permitCode));
            if (hasPermit) {
                log.info(">> 有 hasPermission 设定权限, permissionCode: {}", permissionCode);
                return true;
            }
        }
        return false;
    }
}
